package org.fle4y.app.server.database;

import java.sql.SQLException;
import java.util.Hashtable;

/**
 * author : xiehui<br>
 * company : northking <br>
 * Create at: 2012-6-15<br>
 * @version : 1.0<br>
 * mailto:flexie@foxmail.com<br>
 * Description : 分词器
 */
public class Tokenizer {
	private static final int UNKNOWN = 0, NAME = 1, LONG_NAME = 2, SPECIAL = 3,
			NUMBER = 4, FLOAT = 5, STRING = 6, LONG = 7;

	private static final int QUOTED_IDENTIFIER = 9, REMARK_LINE = 10,
			REMARK = 11;
	private String sql;
	private char cSql[];
	private int length;
	private int index;
	private int type;
	private boolean wait;
	private String token, longNameFirst, longNameLast;
	private static Hashtable keywords=new Hashtable();
	static{
		String keyword[] = {
			    "AND", "ALL", "AVG", "BY", "BETWEEN", "COUNT", "CASEWHEN",
			    "DISTINCT", "DISTINCT", "EXISTS", "EXCEPT", "FALSE", "FROM",
			    "GROUP", "IF", "INTO", "IFNULL", "IS", "IN", "INTERSECT", "INNER",
			    "LEFT", "LIKE", "MAX", "MIN", "NULL", "NOT", "ON", "ORDER", "OR",
			    "OUTER", "PRIMARY", "SELECT", "SET", "SUM", "TO", "TRUE",
			    "UNIQUE", "UNION", "VALUES", "WHERE", "CONVERT", "CAST",
			    "CONCAT", "MINUS", "CALL"
			};

			for (int i = 0; i < keyword.length; i++) {
				keywords.put(keyword[i], keywords);
			}
		
	}
	public Tokenizer(String sql) {
		this.sql = sql;
		this.cSql = sql.toCharArray();
		this.length = cSql.length;
		this.index = 0;
	}
	
	public void back() throws SQLException{
		wait=true;
	}
	public String getString() throws SQLException{
		getToken();
		return token;
	}
	
	/**
	 * 获取名字
	 * @return
	 * @throws SQLException
	 */
	public String getName() throws SQLException{
		getToken();
		if (!wasName()) {
		    throw new SQLException("错误的SQL语句");
		}
		return token;
	}
	
	public boolean wasName() {
		if (type == QUOTED_IDENTIFIER) {
		    return true;
		}
		if (type != NAME) {
		    return false;
		}
		return !keywords.containsKey(token);
	 }
	
	
	/**
	 * 获取指定的字符
	 * @param match
	 * @throws SQLException
	 */
    void getThis(String match) throws SQLException {
    	getToken();
    	if (!token.equals(match)) {
    	    throw new SQLException("未能获取到正确的字符");
    	}
    }

    /**
     * 分词实现
     * @throws SQLException
     */
	public void getToken() throws SQLException {
		if(wait){
			wait=false;
			return;
		}
		token = "";
		/** 跳过空白符 */
		while (index < length && Character.isWhitespace(cSql[index])) {
			index++;
		}
		/** 搜索整个字符串结束未找到 */
		if (index >= length) {
			type = UNKNOWN;
			return;
		}
		/** 找到 */
		char c = cSql[index];

		boolean point = false, digit = false, exp = false, afterexp = false;
		char first = 0;
		StringBuffer name = new StringBuffer();
		boolean end = false;// 是否到末尾

		if (Character.isJavaIdentifierStart(c)) {
			type = NAME;
		} else if ("(),*=;+%".indexOf(c) >= 0) {
			type = SPECIAL;
			index++;
			token = "" + c;
			return;
		} else if (Character.isDigit(c)) {
			type = NUMBER;
			digit=true;
		} else if ("!<>|/-".indexOf(c) >= 0) {
			type = SPECIAL;
			first = c;
		} else if ('\"' == c) {
			type = QUOTED_IDENTIFIER;
		} else if ('\'' == c) {
			type = STRING;
			name.append('\'');
		} else if ('.' == c) {
			type = FLOAT;
			point=true;
		} else {
			throw new SQLException("语句中包含未知的字符.");
		}
		int start = index++;
		while (true) {
			/**到末尾了判断*/
			if (index >= length) {
				c = ' ';
				end = true;
				/** 现在只需要判断这种结尾的情况 */
				if (!(STRING != type && QUOTED_IDENTIFIER != type)) {
					throw new SQLException("语句结尾有非法的字符.");
				}
			}
			/**未到末尾,取出字符*/
			else {
				c = cSql[index];
			}
			switch (type) {
			case NAME:
				if (Character.isJavaIdentifierPart(c)) { // 跳出
					break;
				}
				token = sql.substring(start, index).toUpperCase();
				/** 浮点类型 处理 */
				if ('.' == c) {
					longNameFirst = token;
					index++;
					getToken();
					longNameLast = token;
					type = LONG_NAME;
					token = longNameFirst +"."+ longNameLast;
				}
				return;

			case QUOTED_IDENTIFIER:
				if ('\"' == c) {
					index++;
					if (index >= length) {
						token = name.toString();
						return;
					}
					c = cSql[index];
					if ('.' == c) {
						longNameFirst = name.toString();
						index++;
						getToken();
						longNameLast = token;
						type = LONG_NAME;
						token = longNameFirst + "." + longNameLast;
						return;
					}
					if ('\"' != c) {
						token = name.toString();
						return;
					}
				}
				name.append(c);
				break;

			case STRING:
				if ('\'' == c) {
					index++;
					if (index >= length || '\'' != cSql[index]) {
						token = name.toString();
						return;
					}
				}
				name.append(c);
				break;
				
			case REMARK:
				if (end) {
					type = UNKNOWN;
					return;
				} else if ('*' == c) {
					index++;
					if (index < length && '/' == cSql[index]) {
						index++;
						getToken();
						return;
					}
				}
				break;

			case REMARK_LINE:
				if (end) {
					type = UNKNOWN;
					return;
				} else if ('\r' == c || '\n' == c) {
					getToken();
					return;
				}
				break;

			case SPECIAL:
				if ('/' == c && '/' == first) {
					type =REMARK_LINE;
					break;
				} else if ('-' == c && '-' == first) {
					type =REMARK_LINE;
					break;
				} else if ('*' == c && '/' == first) {
					type = REMARK;
					break;
				} else if (">=|".indexOf(c) >= 0) {
					break;
				}
				token = sql.substring(start, index);
				return;

			case FLOAT:

			case NUMBER:
				if (Character.isDigit(c)) {
					digit = true;
				} else if ('.' == c) {
					type = FLOAT;
					if (point) {
						throw new SQLException("存在错误的字符 . ");
					}
					point = true;
				} else if ('e' == c || 'E' == c) {
					if (exp) {
						throw new SQLException("存在错误的字符 e");
					}
					afterexp = true;
					point = true;
					exp = true;
				} else if ('-' == c && afterexp) {
					afterexp = false;
				} else if ('+' == c && afterexp) {
					afterexp = false;
				} else {
					afterexp = false;
					if (!digit) {
						if (point && start == index - 1) {
							token = ".";
							type = SPECIAL;
							return;
						}
						throw new SQLException("存在错误的字符 ");
					}
					token = sql.substring(start, index);
					return;
				}
			}
			index++;
		}
	}
	
	public int getIndex() {
		return index;
	}

	public int getLength() {
		return length;
	}
	public int getPosition(){
		return index;
	}
}
